package com.crois.barrier.web.configuration;

import com.crois.barrier.common.response.BarrierDefaultResponse;
import com.crois.barrier.common.response.BarrierResponse;
import com.crois.barrier.plugin.BarrierPlugin;
import com.crois.barrier.web.cache.CacheManager;
import com.crois.barrier.web.config.HttpClientProperties;
import com.crois.barrier.web.filter.CrossFilter;
import com.crois.barrier.web.filter.FileSizeFilter;
import com.crois.barrier.web.handler.BarrierWebHandler;
import com.crois.barrier.web.handler.WeightRoundRobinApiRouteMatchHandler;
import com.crois.barrier.web.loadbalance.rule.LoadBalanceRule;
import com.crois.barrier.web.loadbalance.rule.NacosLoadBalancerByServiceName;
import com.crois.barrier.web.loadbalance.rule.WeightRoundRobinLoadBalanceRule;
import com.crois.barrier.web.loadbalance.strategy.DivideStrategy;
import com.crois.barrier.web.loadbalance.strategy.IpListDivideStrategy;
import com.crois.barrier.web.loadbalance.strategy.ServiceNameDivideStrategy;
import com.crois.barrier.web.log.LogSendSvc;
import com.crois.barrier.web.log.LogSendSvcImpl;
import com.crois.barrier.web.plugin.before.AuthPlugin;
import com.crois.barrier.web.plugin.before.RequestPlugin;
import com.crois.barrier.web.plugin.before.IpBlackWhitePlugin;
import com.crois.barrier.web.plugin.before.ParamMappingPlugin;
import com.crois.barrier.web.plugin.post.LogPlugin;
import com.crois.barrier.web.plugin.post.MonitorPlugin;
import com.crois.barrier.web.plugin.post.StatisticsPlugin;
import com.crois.barrier.web.plugin.route.RewritePlugin;
import com.crois.barrier.web.plugin.route.RouteLoadbalancerClientPlugin;
import com.crois.barrier.web.plugin.route.RouteToRequestUrlPlugin;
import com.crois.barrier.web.service.ParamMappingService;
import com.crois.barrier.web.service.impl.DefaultParamMappingService;
import com.crois.barrier.web.statistics.StatisticsSvc;
import com.crois.barrier.web.statistics.StatisticsSvcImpl;
import com.zhimydou.cloud.auth.core.properties.SecurityProperties;
import com.zhimydou.cloud.auth.core.token.service.DefaultTokenServices;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.annotation.Order;
import org.springframework.web.server.WebFilter;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author Hou Ze Yu
 * @description barrier 配置
 * @date 2020/7/13
 */
@Configuration
@Import(value = {CacheConfiguration.class,HttpClientConfiguration.class,SpringCloudConfiguration.class})
@ComponentScan("com.crois.barrier.web")
@EnableConfigurationProperties({HttpClientProperties.class,SecurityProperties.class})
public class BarrierAutoConfiguration {

    private final CacheManager cacheManager;

    @Autowired
    private DefaultTokenServices defaultTokenServices;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Bean
    public LogSendSvc logSendSvc(){
        return new LogSendSvcImpl(rabbitTemplate);
    }

    @Bean
    public StatisticsSvc statisticsSvc(){
        return new StatisticsSvcImpl(rabbitTemplate);
    }

    @Bean
    public BarrierPlugin statisticsPlugin(){
        return new StatisticsPlugin(cacheManager,statisticsSvc());
    }


    @Autowired(required = false)
    public BarrierAutoConfiguration(@Qualifier("localCacheManager") final CacheManager cacheManager) {
        this.cacheManager = cacheManager;
    }
    @Bean
    @ConditionalOnMissingBean(ParamMappingService.class)
    public ParamMappingService paramMappingService(){
        return new DefaultParamMappingService();
    }

    /**
     * monitorPlugin
     * @param cacheManager
     * @return
     */
    @Bean
    public BarrierPlugin monitorPlugin(CacheManager cacheManager){
       return new MonitorPlugin(cacheManager);
    }
    /**
     * paramMappingPlugin
     * @param paramMappingService
     * @return
     */
    @Bean
    public BarrierPlugin paramMappingPlugin(final ParamMappingService paramMappingService){
        return new ParamMappingPlugin(paramMappingService);
    }

    @Bean
    public BarrierPlugin logPlugin(){
        return new LogPlugin(cacheManager,logSendSvc());
    }
    /**
     * RequestPlugin
     * @return
     */
    @Bean
    public BarrierPlugin requestPlugin(){
       return new RequestPlugin(defaultTokenServices);
    }

    /**
     * authPlugin
     * @return
     */
    @Bean
    public BarrierPlugin authPlugin(SecurityProperties securityProperties){
        return new AuthPlugin(cacheManager,defaultTokenServices,securityProperties);
    }

    /**
     *ipBlackWhitePlugin
     * @return
     */
    @Bean
    public BarrierPlugin ipBlackWhitePlugin(){
        return new IpBlackWhitePlugin(cacheManager);
    }
    /***
     * init Rewrite plugin
     * @return
     */
    @Bean
    public BarrierPlugin rewritePlugin(){
        return new RewritePlugin(cacheManager);
    }
    /***
     * init routeToRequestUrl plugin
     * @return
     */
    @Bean
    public BarrierPlugin routeToRequestUrlPlugin(){
       return new RouteToRequestUrlPlugin();
    }
    /***
     * init routeLoadbalancer plugin
     * @return
     */
    @Bean
    public BarrierPlugin routeLoadbalancerClientPlugin(){
        return new RouteLoadbalancerClientPlugin(ipListDivideStrategy(),serviceNameDivideStrategy());
    }
    /**
     * init ipList  Divide  Strategy
     * @return
     */
    @Bean("ipListDivideStrategy")
    public DivideStrategy ipListDivideStrategy(){
        return new IpListDivideStrategy();
    }

    /**
     * init serviceName  Divide  Strategy
     * @return
     */
    @Bean("serviceNameDivideStrategy")
    public DivideStrategy serviceNameDivideStrategy(){
        return new ServiceNameDivideStrategy();
    }
    /**
     * init response
     * @return
     */
     @Bean
     public BarrierResponse doorResponse(){
          return new BarrierDefaultResponse();
     }

    @Bean
     public WeightRoundRobinApiRouteMatchHandler weightRoundRobinApiRouteMatchHandler(){
         return new WeightRoundRobinApiRouteMatchHandler();
     }

     @Bean
     public LoadBalanceRule weightRoundRobinLoadBalanceRule() {
         return new WeightRoundRobinLoadBalanceRule();
     }
     @Bean
     public NacosLoadBalancerByServiceName nacosLoadBalancerByServiceName() {
         return new NacosLoadBalancerByServiceName();
     }
    /***
     * init barrierWebHandler
     * @param plugins
     * @return
     */
    @Bean("webHandler")
    public BarrierWebHandler doorWebHandler(final List<BarrierPlugin> plugins, final CacheManager cacheManager){
            final List<BarrierPlugin> barrierPluginList =plugins.stream().sorted((m, n) -> {
                if (m.pluginType().equals(n.pluginType())){
                       return  m.order() - n.order();
                }else {
                    return m.pluginType().getName().compareTo(n.pluginType().getName());
                }
            }).collect(Collectors.toList());
        return new BarrierWebHandler(barrierPluginList,cacheManager);
    }


    @Bean
    public FileSizeFilter fileSizeFilter(){
        return new FileSizeFilter();
    }


    @Bean
    @Order(-100)
    public WebFilter crossFilter() {
        return new CrossFilter();
    }
}
